﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using System.Reflection;

namespace intelliEssay.Interop
{
    /// <summary>
    /// This class contains method used to retrieve information on document-defined default values.
    /// </summary>
    public static class DocumentSpecificDefault
    {
        /// <summary>
        /// Get a well initialized default or setting for a property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="type"></param>
        /// <param name="wordDoc"></param>
        /// <param name="ha"></param>
        /// <returns></returns>
        public static T GetDefaultOrSettingByPropertyName<T>(WordprocessingDocument wordDoc, HostApplications ha = HostApplications.MsOffice2010)
        {
            return (T)GetDefaultOrSettingByPropertyName(typeof(T).Name, typeof(T), wordDoc, ha);
        }

        /// <summary>
        /// Gets the default values defined by the word document. If the document does not define any defaults, the default values defined by host application is returned.
        /// </summary>
        /// <param name="propertyName">the name of the property whose value is to be returned</param>
        /// <param name="type">该参数用于区分不同命名空间相同名类，若为null，则忽略命名空间</param>
        /// <param name="wordDoc">the wordDoc containing the property</param>
        /// <param name="ha">The host application</param>
        /// <returns>returns the default value</returns>
        /// <remarks>这个函数首先查找MainDocumentPart.DocumentSettingsPart.Settings部分，若无法找到相关设置，则继续调用
        /// ApplicationSpecificDefaults.GetDefaultValueByAppName()，若仍无法找到相应的值，则返回null</remarks>
        /// 
        public static dynamic GetDefaultOrSettingByPropertyName(string propertyName, Type type, WordprocessingDocument wordDoc, HostApplications ha = HostApplications.MsOffice2010)
        {
            dynamic value = _GetDefaultOrSettingByPropertyName(propertyName, type, wordDoc, ha);
            dynamic value2=   ApplicationSpecificDefaults.GetDefaultValueByAppName(ha, propertyName, type);
            Value_Priority_Pair vpp=new Value_Priority_Pair();
            vpp.priority = 0;
            vpp.value = value;
            Value_Priority_Pair vpp2=new Value_Priority_Pair();
            vpp2.priority = 1;
            vpp2.value = value2;
            object finalValue=null;
            InitializationServices.MergeProperties(ref finalValue, new Value_Priority_Pair[] { vpp, vpp2 });
            return finalValue;
        }

        private static dynamic _GetDefaultOrSettingByPropertyName(string propertyName, Type type, WordprocessingDocument wordDoc, HostApplications ha = HostApplications.MsOffice2010)
        {
            if (wordDoc == null)
            {
                throw new ApplicationException("Parameter 'wordDoc' is null");
            }
            dynamic cacheHit = DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wordDoc, propertyName);
            if (cacheHit != null)
            {
                return cacheHit;
            }
            cacheHit = DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wordDoc, "Style-Default");
            Style s = null;
            if (cacheHit == null)
            {
                IEnumerable<Style> styles = (from style in wordDoc.MainDocumentPart.StyleDefinitionsPart.Styles.ChildElements where style.LocalName == "style" select style).Cast<Style>();
                if (ha == HostApplications.MsOffice2010)
                {
                    try
                    {
                        styles = styles.Union<Style>(wordDoc.MainDocumentPart.StylesWithEffectsPart.Styles.ChildElements.Where<OpenXmlElement>(
                        o =>
                        {
                            return o.LocalName == "style";
                        }).Cast<Style>(), new StyleComparer());
                    }
                    catch
                    {
                    }
                    //  styles.Count();
                }
                s = styles.FirstOrDefault<Style>(o =>
                    {
                        return o.Type == StyleValues.Paragraph && o.Default.Value == true;
                    });
                DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, "Style-Default", s);
                DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, "Style-Full", styles);
            }
            else
            {
                s = cacheHit;
            }
            if (s != null)
            {
                if (s.StyleParagraphProperties != null)
                {
                    var pS = s.StyleParagraphProperties;
                    try
                    {
                        dynamic val = pS.GetType().GetProperty(propertyName).GetValue(pS, null);
                        if (val != null)
                        {
                            DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, val);
                            return val;
                        }
                    }
                    catch
                    {
                    }
                }
                if (s.StyleRunProperties != null)
                {
                    var pS = s.StyleRunProperties;
                    try
                    {
                        dynamic val = pS.GetType().GetProperty(propertyName).GetValue(pS, null);
                        if (val != null)
                        {
                            DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, val);
                            return val;
                        }
                    }
                    catch
                    {
                    }
                }
            }
            Settings settings = wordDoc.MainDocumentPart.DocumentSettingsPart.Settings;
            try
            {
                dynamic result = settings.GetType().GetProperty(propertyName).GetValue(settings, null);
                if (result != null)
                {
                    DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, result);
                    return result;
                }
            }
            catch
            {
            }

            dynamic result1 = settings.ChildElements.Where<OpenXmlElement>(o =>
                {
                    return o.GetType().Name == propertyName;
                });
            if (result1.GetType().Name == propertyName)
            {
                if (result1 != null)
                {
                    DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, result1);
                    return result1;
                }
            }
            DocDefaults docDefaults = wordDoc.MainDocumentPart.StyleDefinitionsPart.Styles.DocDefaults;
            var rpd = docDefaults.RunPropertiesDefault.RunPropertiesBaseStyle;
            var ppd = docDefaults.ParagraphPropertiesDefault.ParagraphPropertiesBaseStyle;
            try
            {
                dynamic result2 = rpd.GetType().GetProperty(propertyName).GetValue(rpd, null);
                if (result2 != null)
                {
                    DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, result2);
                    return result2;
                }
            }
            catch
            {
            }
            try
            {
                dynamic result3 = ppd.GetType().GetProperty(propertyName).GetValue(ppd, null);
                if (result3 != null)
                {
                    DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, result3);
                    return result3;
                }
            }
            catch
            {
            }
            dynamic val1 = ApplicationSpecificDefaults.GetDefaultValueByAppName(ha, propertyName, null);
            if (val1 != null)
            {
                DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wordDoc, propertyName, val1);
            }
            return val1;
        }
    }
}
